package com.example.distributelock.lock;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Collections;
import java.util.List;

/**
 * Watcher：Zookeeper的观察器接口
 */
public class ZkLock implements Watcher, AutoCloseable {

    public static final Logger log = LoggerFactory.getLogger(ZkLock.class);

    private final ZooKeeper zooKeeper;
    private final String businessName;
    private String znode;

    public ZkLock(String connectString, String businessName) throws IOException {
        this.zooKeeper = new ZooKeeper(connectString,30000,this);
        this.businessName = businessName;
    }

    public boolean getLock() throws KeeperException, InterruptedException {
        Stat existsNode = zooKeeper.exists("/" + businessName, false);
        if (existsNode == null){
            /**
             * create()方法：
             *    参数一：节点路径
             *    参数二：节点初始化内容
             *    参数三：权限
             *    参数四：创建模式，临时、持久、临时有序、持久有序四种模式
             */
            // 创建业务根节点
            zooKeeper.create("/" + businessName, businessName.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        }
        // 创建临时有序的锁节点，例如：/order/order_00000001
        znode = zooKeeper.create("/" + businessName + "/" + businessName + "_", businessName.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        znode = znode.substring(znode.lastIndexOf("/") + 1);
        // 获取业务根节点下所有子节点
        List<String> childrenNodes = zooKeeper.getChildren("/" + businessName, false);
        // 子节点排序
        Collections.sort(childrenNodes);
        // 获取序号最小的（第一个）子节点
        String firstNode = childrenNodes.get(0);
        // 如果创建的节点不是第一个子节点，则监听前一个节点
        if (!firstNode.equals(znode)) {
            String lastNode = firstNode;
            for (String node : childrenNodes) {
                if (!znode.equals(node)) {
                    lastNode = node;
                } else {
                    zooKeeper.exists("/" + businessName + "/" + lastNode,true);
                    break;
                }
            }
            // wait()方法使用的时候一定要加上synchronized锁，这是java里面固定的写法
            // 等待前面的节点被删除，调用process()方法唤醒线程
            synchronized (this) {
                wait();
            }
        }
        // 如果创建的节点是第一个子节点，则获得锁
        return true;
    }

    /**
     * 如果监听器的节点消失就会调用该方法唤醒线程
     * @param watchedEvent
     */
    @Override
    public void process(WatchedEvent watchedEvent) {
        if (watchedEvent.getType() == Event.EventType.NodeDeleted){
            synchronized (this){
                notify();
            }
        }
    }


    @Override
    public void close() throws Exception {
        zooKeeper.delete("/" + businessName + "/" + znode, -1);
        zooKeeper.close();
        log.info("我释放了锁");
    }
}
